package com.dongzili.demo.common.config.mybatisplus;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dongzili.demo.common.annotation.MapperAndService;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.annotation.AnnotationValue;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.util.*;

@Configuration
@Slf4j
@AutoConfigureAfter(SpringUtil.class)
public class MybatisPlusMapperConfig {
 
    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    private static final List<String> BASE_PACKAGES = Arrays.asList(
            "com.dongzili.demo.customer.model","com.dongzili.demo.customer.redis.model"
    );
 
    // 根据类名获取 bean name
    private String getBeanName(String className) {
 
        int index = className.lastIndexOf(".");
        String simpleClassName = index != -1 ? className.substring(index + 1) : className;
 
        char firstChar = simpleClassName.charAt(0);
        if (firstChar >= 'A' && firstChar <= 'Z') {
            firstChar -= 'A' - 'a';
        }
        return firstChar + simpleClassName.substring(1);
    }
 
    @PostConstruct
    public void setup() {
        for (String packageName : BASE_PACKAGES){
            if(!StringUtils.hasText(packageName)){
                continue;
            }
            // 扫描数据库实体，根据实体创建对应 mapper、service
            Set<Class<?>> entityClassSet = ClassUtil.scanPackage(packageName);
            if (entityClassSet.isEmpty()) {
                continue;
            }
            for (Class<?> entityClass : entityClassSet) {
                // 只创建带有 MapAndService 注解的实体
                MapperAndService tableName = entityClass.getAnnotation(MapperAndService.class);
                if (tableName == null) {
                    continue;
                }
                String mapperClassName = packageName + ".mapper" + "." + entityClass.getSimpleName() + "AutoMapper";
                String serviceImplClassName = packageName + ".service" + "." + entityClass.getSimpleName() + "AutoServiceImpl";
                String serviceClassName = packageName + ".service" + "." + entityClass.getSimpleName() + "AutoService";
                /* 创建 mapper */
                Class<?> mapperClass = new ByteBuddy()
                        .makeInterface(TypeDescription.Generic.Builder.parameterizedType(BaseMapper.class, entityClass).build())
                        .name(mapperClassName)
                        .annotateType(AnnotationDescription.Builder.ofType(Mapper.class).build())
                        .make()
                        .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                        .getLoaded();
                MapperFactoryBean<?> factoryBean = new MapperFactoryBean<>(mapperClass);
                factoryBean.setSqlSessionFactory(sqlSessionFactory);
                sqlSessionFactory.getConfiguration().addMapper(mapperClass);
                try {
                    SpringUtil.registerBean(getBeanName(mapperClassName), factoryBean.getObject());
                    log.info("自动注册 mybatisplus mapper bean -> 名称:{}, 类名：{}", getBeanName(mapperClassName), mapperClassName);
                } catch (Exception e) {
                    log.error("自动注册 mybatisplus mapper bean 失败", e);
                    e.printStackTrace();
                }

                /* 创建 service */
                Class<?> serviceClass = new ByteBuddy()
                        .makeInterface(TypeDescription.Generic.Builder.parameterizedType(IService.class, entityClass).build())
                        .name(serviceClassName)
                        .make()
                        .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                        .getLoaded();

                List<AnnotationDescription> annotations = new ArrayList<>();
                annotations.add(AnnotationDescription.Builder.ofType(Service.class).build());
                annotations.add(AnnotationDescription.Builder.ofType(Transactional.class).build());
                Class<?> serviceImplClass = new ByteBuddy()
                        .subclass(TypeDescription.Generic.Builder.parameterizedType(ServiceImpl.class, mapperClass, entityClass).build())
                        .implement(serviceClass)
                        .name(serviceImplClassName)
                        .annotateType(annotations)
                        .make()
                        .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                        .getLoaded();
                try {
                    SpringUtil.registerBean(getBeanName(serviceImplClassName), serviceImplClass.newInstance());
                    log.info("自动注册 mybatisplus Service Bean -> 名称:{}, 类名：{}", getBeanName(serviceImplClassName), serviceImplClassName);
                } catch (InstantiationException | IllegalAccessException e) {
                    log.error("自动注册 mybatisplus Service Bean 失败", e);
                    e.printStackTrace();
                }
            }
        }
    }
}